import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import tkinter.font as tkfont
import re
import os


class EditorTab(ttk.Frame):
    def __init__(self, master, main_app):
        super().__init__(master)
        self.main_app = main_app
        self.current_file = None
        self.is_modified = False
        self.file_type = None
        self.highlighted_line = None
        self.font_family = 'Consolas'
        self.font_size = 10
        self.font = tkfont.Font(
            family=self.font_family,
            size=self.font_size
        )
        
        # 增加字体大小限制
        self.min_font_size = 8
        self.max_font_size = 36
        self.line_height = self.font.metrics()['linespace']
        
        self.create_widgets()
        self.bind_events()
        self.setup_syntax_highlighting()
        self.create_context_menu()

        self.text_area.focus_set()
        self.text_area.mark_set(tk.INSERT, "1.0")
        self.highlight_current_line()
        self.text_area.edit_modified(False)
        self.update_line_numbers()
        

    def create_widgets(self):
        self.line_numbers = tk.Canvas(
            self, width=50, background='#f0f0f0',
            highlightthickness=0, borderwidth=0
        )
        self.line_numbers.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 3))
        
        self.text_area = tk.Text(
            self, wrap=tk.NONE, undo=True,
            font=self.font, padx=5,
            borderwidth=0, highlightthickness=0,
            tabs=(self.font.measure('    '), 'left')
        )
        self.text_area.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        self.scroll = ttk.Scrollbar(self)
        self.scroll.pack(side=tk.RIGHT, fill=tk.Y)
        
        self.text_area.config(yscrollcommand=self.on_scroll)
        self.scroll.config(command=self.sync_scroll)
        
        self.text_area.tag_config('highlight', background='#fff3d0')
        self.text_area.tag_config('current_line', background='#e6f3ff')
        self.text_area.tag_config('search_highlight', background='yellow')
        
        self.text_area.tag_raise('highlight')  # 确保在语法高亮之上
        self.text_area.tag_lower('search_highlight')  # 搜索高亮在下方

    def setup_syntax_highlighting(self):
        self.syntax_rules = {
            'py': {
                'keywords': r'\b(and|as|assert|break|class|continue|def|del|elif|else|except|finally|for|from|global|if|import|in|is|lambda|nonlocal|not|or|pass|raise|return|try|while|with|yield|True|False|None)\b',
                'strings': r'(\".*?\"|\'.*?\')',
                'comments': r'#[^\n]*',
                'numbers': r'\b[0-9]+\b',
                'functions': r'\bdef\s+(\w+)',
                'classes': r'\bclass\s+(\w+)'
            },
            'c': {
                'keywords': r'\b(auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b',
                'strings': r'(\".*?\")',
                'comments': r'\/\/[^\n]*|\/\*.*?\*\/',
                'numbers': r'\b[0-9]+\b',
                'functions': r'\b\w+\s+\w+\s*\([^)]*\)\s*\{',
                'preprocessor': r'#\s*\w+'
            },
            'cpp': {
                'keywords': r'\b(auto|break|case|catch|class|const|continue|'
                            r'default|delete|do|double|else|enum|explicit|export|'
                            r'false|for|friend|goto|if|inline|mutable|namespace|'
                            r'new|nullptr|operator|private|protected|public|'
                            r'return|static|struct|switch|template|this|throw|'
                            r'true|try|typedef|typeid|typename|union|using|'
                            r'virtual|void|volatile|while)\b',
                'types': r'\b(bool|char|char16_t|char32_t|double|float|int|long|'
                         r'short|signed|size_t|ssize_t|unsigned|wchar_t)\b',
                'strings': r'(\".*?\"|R\"([^()\\ ]{0,16})\(.*?\)\2\")',
                'comments': r'\/\/[^\n]*|\/\*.*?\*\/',
                'numbers': r'\b[0-9]+\b',
                'preprocessor': r'#\s*\w+',
                'functions': r'\b\w+\s+\w+\s*\([^)]*\)\s*(\{|;)'
            },
            'java': {
                'keywords': r'\b(abstract|assert|boolean|break|byte|case|catch|'
                            r'char|class|const|continue|default|do|double|else|'
                            r'enum|extends|final|finally|float|for|goto|if|'
                            r'implements|import|instanceof|int|interface|long|'
                            r'native|new|package|private|protected|public|return|'
                            r'short|static|strictfp|super|switch|synchronized|'
                            r'this|throw|throws|transient|try|void|volatile|while)\b',
                'types': r'\b(boolean|byte|char|double|float|int|long|short|'
                         r'String|var|void)\b',
                'strings': r'(\".*?\")',
                'comments': r'\/\/[^\n]*|\/\*.*?\*\/',
                'annotations': r'@\w+',
                'numbers': r'\b[0-9]+\b',
                'functions': r'\b(public|protected|private|static|\s+) +[\w<>]+\s+(\w+) *\([^\)]*\)'
            },
            'gitignore': {
                'comments': r'^#.*',
                'negation': r'^!.*',
                'directory': r'\/$',
                'patterns': r'^\s*[\w*?.*]+\s*$'
            },
            'md': {
                'headers': r'^#{1,6}\s.*',
                'bold': r'\*\*.*?\*\*|__.*?__',
                'italic': r'\*.*?\*|_.*?_',
                'lists': r'^(\s*[-*+]|\s*\d+\.)\s',
                'code': r'`{3}.*?`{3}|`.*?`',
                'links': r'\[.*?\]\(.*?\)',
                'images': r'!\[.*?\]\(.*?\)'
            }
        }
        
        self.highlight_colors = {
            'keywords': {'foreground': '#0000FF', 'font': ('Consolas', 10, 'bold')},
            'strings': {'foreground': '#008000', 'font': ('Consolas', 10)},
            'comments': {'foreground': '#808080', 'font': ('Consolas', 10, 'italic')},
            'numbers': {'foreground': '#FF00FF', 'font': ('Consolas', 10)},
            'functions': {'foreground': '#008080', 'font': ('Consolas', 10, 'bold')},
            'classes': {'foreground': '#000080', 'font': ('Consolas', 10, 'bold underline')},
            'preprocessor': {'foreground': '#800080', 'font': ('Consolas', 10)},
            'types': {'foreground': '#2B91AF', 'font': ('Consolas', 10, 'bold')},
            'annotations': {'foreground': '#808000', 'font': ('Consolas', 10, 'italic')},
            'negation': {'foreground': '#FF0000', 'font': ('Consolas', 10)},
            'directory': {'foreground': '#000080', 'font': ('Consolas', 10)},
            'patterns': {'foreground': '#008000', 'font': ('Consolas', 10)},
            'headers': {'foreground': '#000080', 'font': ('Consolas', 12, 'bold')},
            'bold': {'foreground': '#000000', 'font': ('Consolas', 10, 'bold')},
            'italic': {'foreground': '#000000', 'font': ('Consolas', 10, 'italic')},
            'lists': {'foreground': '#800080', 'font': ('Consolas', 10)},
            'code': {'foreground': '#008000', 'font': ('Consolas', 10)},
            'links': {'foreground': '#0000FF', 'font': ('Consolas', 10, 'underline')},
            'images': {'foreground': '#800080', 'font': ('Consolas', 10)}
        }
        
        for tag_name, style in self.highlight_colors.items():
            self.text_area.tag_config(tag_name, **style)

    def bind_events(self):
        self.text_area.bind('<<Modified>>', self.on_text_change)
        events = [
            '<KeyRelease>', '<MouseWheel>', '<Configure>',
            '<ButtonRelease-1>', '<<Paste>>', '<<Undo>>', '<<Redo>>',
            '<Button-3>',  # 绑定右键点击
        ]
        for event in events:
            if event == '<Button-3>':
                self.text_area.bind(event, self.show_context_menu)
            else:
                self.text_area.bind(event, lambda e: self.update_line_numbers())
        
        self.text_area.bind('<Tab>', self.insert_tab)
        self.text_area.bind('<KeyPress>', self.on_key_press)
        self.text_area.bind('<Return>', self.handle_return)
        self.text_area.bind('<Control-MouseWheel>', self.adjust_font_size)
        self.line_numbers.bind('<Control-MouseWheel>', self.adjust_font_size)

    def handle_return(self, event=None):
        """处理回车键的自动缩进（支持多语言）"""
        current_index = self.text_area.index(tk.INSERT)
        line, col = current_index.split('.')
        
        line_start = f"{line}.0"
        line_end = f"{line}.end"
        line_content = self.text_area.get(line_start, line_end).rstrip('\n')
        
        before_caret = self.text_area.get(line_start, f"{line}.{col}")
        indent = len(before_caret) - len(before_caret.lstrip())
        new_indent = ' ' * indent
        
        # 根据文件类型确定缩进规则
        if self.file_type in ['py'] and line_content.rstrip().endswith(':'):
            new_indent += '    '
        elif self.file_type in ['java', 'cpp'] and '{' in line_content:
            new_indent += '    '
        
        after_caret = self.text_area.get(f"{line}.{col}", line_end)
        self.text_area.delete(f"{line}.{col}", line_end)
        self.text_area.insert(tk.INSERT, f"\n{new_indent}{after_caret}")
        
        new_line = str(int(line) + 1)
        target_col = len(new_indent)
        self.text_area.mark_set(tk.INSERT, f"{new_line}.{target_col}")
        
        return "break"

    def adjust_font_size(self, event=None):
        """Windows专用字体调整"""
        # 仅处理鼠标滚轮事件
        if not event or not hasattr(event, 'delta'):
            return

        # 根据delta判断方向（Windows下delta为±120）
        direction = 1 if event.delta > 0 else -1

        # 计算新字体大小
        new_size = self.font_size + direction
        if new_size < self.min_font_size or new_size > self.max_font_size:
            return

        # 更新字体配置
        self.font_size = new_size
        self.font.configure(size=self.font_size)
    
        # 更新语法高亮标签
        for tag_name, style in self.highlight_colors.items():
            new_style = style.copy()
            # 直接操作元组，无需split()
            original_font = style['font']
            # 确保元组格式正确，例如 ('Consolas', 10, 'bold italic')
            new_font = (
                original_font[0],  # 字体名称
                self.font_size,    # 新字号
                *original_font[2:] # 保留后续样式（如 bold/italic）
            )
            new_style['font'] = new_font
            self.text_area.tag_config(tag_name, **new_style)
        
        self.apply_syntax_highlighting()

        # 重新计算布局
        self.line_height = self.font.metrics()['linespace']
        tab_width = self.font.measure('    ')
        self.text_area.configure(tabs=(tab_width, 'left'))

        # 刷新界面
        self.update_line_numbers()
        self.highlight_current_line()
        self.apply_syntax_highlighting()        
            
    def is_comment_or_string(self, line):
        stripped = line.strip()
        return (
            stripped.startswith('#') or
            any(c in line for c in ('"', "'", '//', '/*', '*/'))
        )

    def create_context_menu(self):
        self.context_menu = tk.Menu(self.text_area, tearoff=0)
        self.context_menu.add_command(label="复制", command=self.copy_text)
        self.context_menu.add_command(label="剪切", command=self.cut_text)
        self.context_menu.add_command(label="粘贴", command=self.paste_text)
        self.context_menu.add_separator()
        self.context_menu.add_command(label="全选", command=self.select_all)

    def show_context_menu(self, event):
        try:
            self.text_area.index(tk.SEL_FIRST)
            has_selection = True
        except tk.TclError:
            has_selection = False
        
        self.context_menu.entryconfig("复制", state=tk.NORMAL if has_selection else tk.DISABLED)
        self.context_menu.entryconfig("剪切", state=tk.NORMAL if has_selection else tk.DISABLED)
        self.context_menu.post(event.x_root, event.y_root)

    def copy_text(self):
        self.text_area.event_generate("<<Copy>>")

    def cut_text(self):
        self.text_area.event_generate("<<Cut>>")
        self.on_text_change()

    def paste_text(self):
        self.text_area.event_generate("<<Paste>>")
        self.on_text_change()

    def select_all(self):
        self.text_area.tag_add(tk.SEL, "1.0", tk.END)
        self.text_area.mark_set(tk.INSERT, "1.0")
        self.text_area.see(tk.INSERT)
        self.update_line_numbers()

    def insert_tab(self, event=None):
        self.text_area.insert(tk.INSERT, "    ")
        return "break"

    def on_key_press(self, event=None):
        self.highlight_current_line()
        if self.highlighted_line:
            current = int(self.text_area.index(tk.INSERT).split('.')[0])
            if current != self.highlighted_line:
                self.text_area.tag_remove('highlight', 
                                        f"{self.highlighted_line}.0", 
                                        f"{self.highlighted_line}.end")
                self.highlighted_line = None

    def on_text_change(self, event=None):
        if self.text_area.edit_modified():
            self.is_modified = True
            self.text_area.edit_modified(False)
            
        self.update_line_numbers()
        self.main_app.update_status(self)
        self.main_app.update_title(self)
        self.apply_syntax_highlighting()

    def apply_syntax_highlighting(self):
        if not self.file_type or self.file_type not in self.syntax_rules:
            return
            
        for tag in self.highlight_colors.keys():
            self.text_area.tag_remove(tag, '1.0', 'end')
        
        text = self.text_area.get('1.0', 'end-1c')
        if not text:
            return
            
        rules = self.syntax_rules[self.file_type]
        for category, pattern in rules.items():
            if category not in self.highlight_colors:
                continue
                
            for match in re.finditer(pattern, text, re.MULTILINE | re.DOTALL):
                start = match.start()
                end = match.end()
                self.text_area.tag_add(
                    category,
                    f"1.0 + {start} chars",
                    f"1.0 + {end} chars"
                )
        
        self.highlight_current_line()

    def highlight_current_line(self):
        self.text_area.tag_remove('current_line', '1.0', 'end')
        if self.text_area.get('1.0', 'end-1c'):
            current = self.text_area.index(tk.INSERT)
            line = current.split('.')[0]
            self.text_area.tag_add('current_line', f"{line}.0", f"{line}.end")

    def on_scroll(self, *args):
        self.line_numbers.yview_moveto(args[0])
        self.update_line_numbers()
        
    def sync_scroll(self, *args):
        self.text_area.yview(*args)
        self.line_numbers.yview(*args)
        
    def update_line_numbers(self, event=None):
        first_visible = int(self.text_area.index("@0,0").split('.')[0])
        last_visible = int(self.text_area.index(f"@0,{self.text_area.winfo_height()}").split('.')[0])
        end_index = self.text_area.index('end-1c')
        total = int(end_index.split('.')[0]) if end_index != '1.0' else 1
        
        start = max(1, first_visible - 2)
        end = min(total, last_visible + 2)
        
        self.line_numbers.delete("all")
        for line in range(start, end + 1):
            bbox = self.text_area.bbox(f"{line}.0")
            if bbox:
                y = bbox[1] + (self.line_height - self.font.metrics()['ascent']) // 2
                self.line_numbers.create_text(
                    45, y,
                    text=str(line),
                    anchor=tk.NE,
                    font=(self.font_family, self.font_size), 
                    fill='#666'
                )

    def jump_to_line(self, line_num):
        end_index = self.text_area.index('end-1c')
        max_line = int(end_index.split('.')[0]) if end_index != '1.0' else 1
        target = max(1, min(int(line_num), max_line))
        
        if self.highlighted_line:
            self.text_area.tag_remove('highlight', 
                                    f"{self.highlighted_line}.0", 
                                    f"{self.highlighted_line}.end")
        
        pos = f"{target}.0"
        self.text_area.mark_set(tk.INSERT, pos)
        self.text_area.see(pos)
        self.text_area.tag_add('highlight', pos, f"{target}.end")
        self.highlighted_line = target
        
        self.update_line_numbers()
        self.main_app.update_status(self)

    def delete_lines(self, direction):
        try:
            num_lines = int(self.main_app.del_lines_entry.get())
            if num_lines <= 0:
                messagebox.showerror("错误", "请输入大于0的行数")
                return
                
            current_line = int(self.text_area.index(tk.INSERT).split('.')[0])
            total_lines = int(self.text_area.index('end-1c').split('.')[0])
            
            if direction == -1:
                start = max(1, current_line - num_lines + 1)
                end = current_line
            else:
                start = current_line
                end = min(total_lines, current_line + num_lines - 1)
            
            self.text_area.delete(f"{start}.0", f"{end+1}.0")
            self.text_area.edit_modified(True)
            self.main_app.update_title(self)
            
            new_line = start if direction == -1 else min(start, total_lines - (end - start))
            self.text_area.mark_set(tk.INSERT, f"{new_line}.0")
            self.highlight_current_line()
            self.text_area.focus_set()  
            
        except ValueError:
            messagebox.showerror("错误", "请输入有效的行数")


class SearchReplaceDialog(tk.Toplevel):
    def __init__(self, parent, main_app, last_search, last_replace):
        super().__init__(parent)
        self.main_app = main_app
        self.last_search = last_search  
        self.last_replace = last_replace
        self.title("查换")
        self.last_search_pos = '1.0'
        self.current_match = None
        self.match_count = 0
        
        self.set_dialog_position()
        self.create_widgets()
        self.transient(parent)
        self.grab_set()
        self.search_entry.insert(0, self.last_search)
        self.replace_entry.insert(0, self.last_replace)
        
        # 自动全选查找内容
        self.search_entry.selection_range(0, tk.END)
        self.search_entry.icursor(tk.END)  # 光标移动到末尾
        self.search_entry.focus_set()
        
        # 绑定回车键自动查找
        self.search_entry.bind("<Return>", lambda e: self.find_next())
        
    def update_search_history(self):
        """更新主应用的搜索记录"""
        self.main_app.last_search = self.search_entry.get().strip()
        self.main_app.last_replace = self.replace_entry.get().strip()

    def set_dialog_position(self):
        screen_width = self.winfo_screenwidth()
        screen_height = self.winfo_screenheight()
        dialog_width = 380
        dialog_height = 230
        x = screen_width - dialog_width - 170
        y = 100
        self.geometry(f"+{x}+{y}")

    def create_widgets(self):
        frame = ttk.Frame(self)
        frame.pack(padx=10, pady=10)

        input_frame = ttk.Frame(frame)
        input_frame.grid(row=0, column=0, columnspan=2, sticky=tk.EW)
        
        ttk.Label(input_frame, text="查找内容:").grid(row=0, column=0, sticky=tk.W)
        self.search_entry = ttk.Entry(input_frame, width=30)
        self.search_entry.focus_set()
        self.search_entry.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W)
        
        ttk.Label(input_frame, text="替换为:").grid(row=1, column=0, sticky=tk.W)
        self.replace_entry = ttk.Entry(input_frame, width=30)
        self.replace_entry.grid(row=1, column=1, padx=5, pady=5, sticky=tk.W)
        
        swap_btn = ttk.Button(input_frame, text="↔", width=3, command=self.swap_content)
        swap_btn.grid(row=0, column=2, rowspan=2, padx=5, pady=5)

        self.match_count_var = tk.StringVar()
        self.match_count_var.set("匹配数: 0")
        ttk.Label(frame, textvariable=self.match_count_var).grid(
            row=2, column=0, columnspan=2, sticky=tk.W)

        self.case_sensitive = tk.BooleanVar()
        self.whole_word = tk.BooleanVar()
        ttk.Checkbutton(frame, text="区分大小写", variable=self.case_sensitive,
                        command=self.update_match_count).grid(
            row=3, column=0, columnspan=2, sticky=tk.W)
        ttk.Checkbutton(frame, text="全词匹配", variable=self.whole_word,
                       command=self.update_match_count).grid(
            row=4, column=0, columnspan=2, sticky=tk.W)

        btn_frame = ttk.Frame(frame)
        btn_frame.grid(row=5, column=0, columnspan=2, pady=5)
        
        ttk.Button(btn_frame, text="查找上一个", command=self.find_previous).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="查找下一个", command=self.find_next).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="替换", command=self.replace).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="全部替换", command=self.replace_all).pack(side=tk.LEFT, padx=2)
        ttk.Button(btn_frame, text="关闭", command=self.destroy).pack(side=tk.RIGHT, padx=2)

        self.search_entry.bind('<KeyRelease>', lambda e: self.update_match_count())

    def update_match_count(self, event=None):
        editor = self.main_app.get_current_editor()
        if not editor:
            return

        search_text = self.search_entry.get().strip()
        if not search_text:
            self.match_count_var.set("匹配数: 0")
            self.match_count = 0
            return

        try:
            pattern = re.escape(search_text)
            if self.whole_word.get():
                pattern = rf'\b{pattern}\b'
            flags = 0 if self.case_sensitive.get() else re.IGNORECASE
            regex = re.compile(pattern, flags)

            content = editor.text_area.get('1.0', 'end-1c')
            matches = list(regex.finditer(content))
            self.match_count = len(matches)
            self.match_count_var.set(f"匹配数: {self.match_count}")
        except:
            self.match_count_var.set("匹配数: 0")
            self.match_count = 0

    def swap_content(self):
        search_text = self.search_entry.get()
        replace_text = self.replace_entry.get()
        
        self.search_entry.delete(0, tk.END)
        self.search_entry.insert(0, replace_text)
        self.replace_entry.delete(0, tk.END)
        self.replace_entry.insert(0, search_text)
        
        self.search_entry.focus_set()
        self.update_match_count()

    def find_previous(self):
        self.update_search_history()
        editor = self.main_app.get_current_editor()
        if not editor:
            return False

        params = self.get_search_params()
        search_text = params['search_text']
        if not search_text:
            return False

        pattern = re.escape(search_text)
        if params['whole_word']:
            pattern = rf'\b{pattern}\b'
        flags = 0 if params['case_sensitive'] else re.IGNORECASE
        regex = re.compile(pattern, flags)

        content = editor.text_area.get('1.0', tk.INSERT)
        matches = list(regex.finditer(content))
        
        if matches:
            last_match = matches[-1]
            start = editor.text_area.index(f"1.0+{last_match.start()}c")
            end = editor.text_area.index(f"1.0+{last_match.end()}c")
            self.last_search_pos = start
            self.current_match = (start, end)
            self.highlight_match(editor, start, end)
            return True
        else:
            messagebox.showinfo("查找", "找不到更多匹配项。")
            return False

    def find_next(self, from_start=False):
        self.update_search_history()
        editor = self.main_app.get_current_editor()
        if not editor:
            return False

        params = self.get_search_params()
        search_text = params['search_text']
        if not search_text:
            return False

        pattern = re.escape(search_text)
        if params['whole_word']:
            pattern = rf'\b{pattern}\b'
        flags = 0 if params['case_sensitive'] else re.IGNORECASE
        regex = re.compile(pattern, flags)

        start_idx = '1.0' if from_start else self.last_search_pos
        content = editor.text_area.get(start_idx, "end-1c")
        match = regex.search(content)
        
        if match:
            start = editor.text_area.index(f"{start_idx}+{match.start()}c")
            end = editor.text_area.index(f"{start_idx}+{match.end()}c")
            self.last_search_pos = end
            self.current_match = (start, end)
            self.highlight_match(editor, start, end)
            return True
        else:
            if not from_start and start_idx != '1.0':
                return self.find_next(from_start=True)
            messagebox.showinfo("查找", "找不到更多匹配项。")
            return False

    def highlight_match(self, editor, start, end):
        editor.text_area.tag_remove('search_highlight', '1.0', 'end')
        editor.text_area.tag_add('search_highlight', start, end)
        editor.text_area.mark_set(tk.INSERT, start)
        editor.text_area.see(start)
        editor.text_area.tag_add(tk.SEL, start, end)
        editor.highlight_current_line()

    def replace(self):
        self.update_search_history()
        editor = self.main_app.get_current_editor()
        if not editor or not self.current_match:
            if not self.find_next():
                messagebox.showinfo("替换", "没有找到匹配项")
            return

        replace_text = self.replace_entry.get()
        start, end = self.current_match
        
        editor.text_area.delete(start, end)
        editor.text_area.insert(start, replace_text)
        editor.is_modified = True
        self.main_app.update_title(editor)
        
        self.last_search_pos = editor.text_area.index(f"{start}+{len(replace_text)}c")
        self.update_match_count()
        self.find_next()

    def replace_all(self):
        self.update_search_history()
        editor = self.main_app.get_current_editor()
        if not editor:
            return

        params = self.get_search_params()
        replace_text = self.replace_entry.get().strip()
        search_text = params['search_text']
        if not search_text:
            return

        pattern = re.escape(search_text)
        if params['whole_word']:
            pattern = rf'\b{pattern}\b'
        flags = 0 if params['case_sensitive'] else re.IGNORECASE
        regex = re.compile(pattern, flags)

        content = editor.text_area.get('1.0', 'end-1c')
        matches = list(regex.finditer(content))
        if not matches:
            messagebox.showinfo("替换", "找不到匹配项。")
            return

        count = 0
        editor.text_area.tag_remove('search_highlight', '1.0', 'end')
        for match in reversed(matches):
            start = match.start()
            end = match.end()
            start_pos = editor.text_area.index(f"1.0+{start}c")
            end_pos = editor.text_area.index(f"1.0+{end}c")
            editor.text_area.delete(start_pos, end_pos)
            editor.text_area.insert(start_pos, replace_text)
            count += 1

        editor.is_modified = True
        self.main_app.update_title(editor)
        messagebox.showinfo("替换", f"共替换了{count}处匹配项。")
        self.last_search_pos = '1.0'
        self.current_match = None
        self.update_match_count()

    def get_search_params(self):
        return {
            'search_text': self.search_entry.get().strip(),
            'case_sensitive': self.case_sensitive.get(),
            'whole_word': self.whole_word.get()
        }

    def destroy(self):
        editor = self.main_app.get_current_editor()
        if editor:
            editor.text_area.tag_remove('search_highlight', '1.0', 'end')
            try:
                editor.text_area.tag_remove(tk.SEL, '1.0', 'end')
            except:
                pass
        super().destroy()


class MainApplication:
    def __init__(self, root):
        self.last_search = ""  # 新增
        self.last_replace = ""  # 新增
        self.root = root
        self.root.title("极码本")
        self.create_toolbar()
        self.create_notebook()
        self.create_statusbar()
        self.add_new_tab()
        self.bind_global_events()

    def create_toolbar(self):
        self.toolbar = ttk.Frame(self.root)
        self.toolbar.pack(fill=tk.X, padx=2, pady=2)
        
        ttk.Button(self.toolbar, text="新建 (Ctrl+N)", command=self.add_new_tab).pack(side=tk.LEFT)
        ttk.Button(self.toolbar, text="打开 (Ctrl+O)", command=self.open_file).pack(side=tk.LEFT, padx=5)
        ttk.Button(self.toolbar, text="保存 (Ctrl+S)", command=self.save_file).pack(side=tk.LEFT)
        ttk.Button(self.toolbar, text="另存为", command=self.save_as_file).pack(side=tk.LEFT)
        ttk.Button(self.toolbar, text="关闭标签 (Ctrl+W)", command=self.close_current_tab).pack(side=tk.LEFT, padx=5)
        
        ttk.Separator(self.toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)
        
        ttk.Button(self.toolbar, text="查换", command=self.open_search_replace).pack(side=tk.LEFT)
        ttk.Separator(self.toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)
        
        ttk.Label(self.toolbar, text="转到行:").pack(side=tk.LEFT)
        self.goto_entry = ttk.Entry(self.toolbar, width=8)
        self.goto_entry.pack(side=tk.LEFT, padx=2)
        ttk.Button(self.toolbar, text="跳转", command=self.jump_to_line).pack(side=tk.LEFT)
        self.goto_entry.bind('<Return>', lambda e: self.jump_to_line())
        
        ttk.Button(self.toolbar, text="⇱ 首行", command=self.jump_to_first_line,
                  width=6).pack(side=tk.LEFT, padx=(5,0))
        ttk.Button(self.toolbar, text="⇲ 尾行", command=self.jump_to_last_line,
                  width=6).pack(side=tk.LEFT)
        
        ttk.Separator(self.toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)
        
        ttk.Button(self.toolbar, text="↑增行", width=6, command=lambda: self.add_line(-1)).pack(side=tk.LEFT, padx=(5,0))
        ttk.Button(self.toolbar, text="↓增行", width=6, command=lambda: self.add_line(1)).pack(side=tk.LEFT)
        ttk.Separator(self.toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)
        
        ttk.Label(self.toolbar, text="删除行数:").pack(side=tk.LEFT)
        self.del_lines_entry = ttk.Entry(self.toolbar, width=5)
        self.del_lines_entry.pack(side=tk.LEFT, padx=2)
        self.del_lines_entry.insert(0, "1")
        ttk.Button(self.toolbar, text="↑删除", command=lambda: self.delete_lines(-1)).pack(side=tk.LEFT)
        ttk.Button(self.toolbar, text="↓删除", command=lambda: self.delete_lines(1)).pack(side=tk.LEFT)
        
        ttk.Separator(self.toolbar, orient=tk.VERTICAL).pack(side=tk.LEFT, padx=5, fill=tk.Y)
        ttk.Button(self.toolbar, text="删至首行", command=lambda: self.delete_to_edge('start')).pack(side=tk.LEFT)
        ttk.Button(self.toolbar, text="删至尾行", command=lambda: self.delete_to_edge('end')).pack(side=tk.LEFT)

    def create_notebook(self):
        self.notebook = ttk.Notebook(self.root)
        self.notebook.pack(fill=tk.BOTH, expand=True)
        self.notebook.bind("<<NotebookTabChanged>>", lambda e: self.on_tab_changed())

    def create_statusbar(self):
        self.status_var = tk.StringVar()
        ttk.Label(self.root, textvariable=self.status_var, 
                 relief=tk.SUNKEN, anchor=tk.W).pack(side=tk.BOTTOM, fill=tk.X)

    def bind_global_events(self):
        self.root.bind('<Control-n>', lambda e: self.add_new_tab())
        self.root.bind('<Control-o>', lambda e: self.open_file())
        self.root.bind('<Control-s>', lambda e: self.save_file())
        self.root.bind('<Control-g>', lambda e: self.goto_entry.focus())
        self.root.bind('<Control-f>', lambda e: self.open_search_replace())
        self.root.bind('<Control-w>', lambda e: self.close_current_tab())
        self.root.protocol("WM_DELETE_WINDOW", self.on_close)

    def get_current_editor(self):
        current_tab = self.notebook.select()
        if not current_tab:
            return None
        return self.notebook.nametowidget(current_tab).winfo_children()[0]

    def add_new_tab(self):
        frame = ttk.Frame(self.notebook)
        editor = EditorTab(frame, self)
        editor.pack(fill=tk.BOTH, expand=True)
        self.notebook.add(frame, text="新文档")
        self.notebook.select(frame)
        return editor

    def open_file(self):
        editor = self.get_current_editor()
        if editor and self.check_save_needed(editor):
            filepath = filedialog.askopenfilename(
                filetypes=[
                    ("C Files", "*.c"),                  # 新增C语言支持
                    ("Python Files", "*.py"),
                    ("C++ Files", "*.cpp *.hpp"),
                    ("Java Files", "*.java"),
                    ("Gitignore", ".gitignore"),
                    ("Markdown", "*.md"),
                    ("Text Files", "*.txt"),
                    ("All Files", "*.*")
                ]
            )
            if filepath:
                try:
                    with open(filepath, 'r', encoding='utf-8') as f:
                        content = f.read()
                    editor.text_area.delete('1.0', tk.END)
                    editor.text_area.insert('1.0', content)
                    editor.current_file = filepath
                    editor.file_type = self.get_file_type(filepath)
                    editor.text_area.edit_modified(False)
                    editor.is_modified = False
                    self.update_title(editor)
                    self.notebook.tab(self.notebook.select(), text=os.path.basename(filepath))
                    editor.apply_syntax_highlighting()
                except Exception as e:
                    messagebox.showerror("打开失败", f"无法打开文件:\n{str(e)}")

    def save_file(self):
        editor = self.get_current_editor()
        if not editor:
            return
            
        if not editor.current_file:
            self.save_as_file()
        else:
            try:
                with open(editor.current_file, 'w', encoding='utf-8') as f:
                    f.write(editor.text_area.get('1.0', tk.END))
                editor.is_modified = False
                self.update_title(editor)
                messagebox.showinfo("保存成功", "文件已保存！")
            except Exception as e:
                messagebox.showerror("保存失败", f"保存文件时出错:\n{str(e)}")

    def save_as_file(self):
        editor = self.get_current_editor()
        if not editor:
            return
            
        default_ext = {
            'cpp': '.cpp',
            'java': '.java',
            'gitignore': '',
            'md': '.md'
        }.get(editor.file_type, '.txt')
        
        filepath = filedialog.asksaveasfilename(
            defaultextension=default_ext,
            filetypes=[
                ("C Files", "*.c"),                  # 新增C语言支持
                ("Python Files", "*.py"), 
                ("C++ Files", "*.cpp"),
                ("Java Files", "*.java"),
                ("Gitignore", ".gitignore"),
                ("Markdown", "*.md"),
                ("Text Files", "*.txt"),
                ("All Files", "*.*")
            ]
        )
        if filepath:
            editor.current_file = filepath
            editor.file_type = self.get_file_type(filepath)
            self.save_file()
            self.notebook.tab(self.notebook.select(), text=os.path.basename(filepath))

    def close_current_tab(self):
        current_tab = self.notebook.select()
        if not current_tab:
            return

        editor = self.notebook.nametowidget(current_tab).winfo_children()[0]
        if not self.check_save_needed(editor):
            return

        self.notebook.forget(current_tab)
        
        if not self.notebook.tabs():
            self.add_new_tab()

    def delete_to_edge(self, edge_type):
        editor = self.get_current_editor()
        if not editor:
            return
        
        try:
            current_line = int(editor.text_area.index(tk.INSERT).split('.')[0])
            total_lines = int(editor.text_area.index('end-1c').split('.')[0])
            
            if edge_type == 'start':
                start = 1
                end = current_line
                new_pos = "1.0"
            else:  # 'end'
                start = current_line
                end = total_lines
                new_pos = f"{end}.0"
            
            editor.text_area.delete(f"{start}.0", f"{end+1}.0")
            editor.text_area.edit_modified(True)
            self.update_title(editor)
            
            editor.text_area.mark_set(tk.INSERT, new_pos)
            editor.highlight_current_line()
            editor.update_line_numbers()
            
        except Exception as e:
            messagebox.showerror("操作错误", f"删除操作失败: {str(e)}")

    def get_file_type(self, filename):
        if not filename:
            return None
        
        # 处理以点开头的特殊文件
        if os.path.basename(filename).startswith('.'):
            name = os.path.basename(filename).lower()
            if name == '.gitignore':
                return 'gitignore'
            if name.endswith('.md'):
                return 'md'
            return None
        
        ext = os.path.splitext(filename)[1][1:].lower()
        type_map = {
            'py': 'py',
            'c': 'c',
            'cpp': 'cpp', 
            'hpp': 'cpp',
            'java': 'java',
            'md': 'md'
        }
        return type_map.get(ext, None)

    def check_save_needed(self, editor):
        if not editor.is_modified:
            return True
            
        answer = messagebox.askyesnocancel("保存更改", "当前文件已修改，是否保存更改？")
        if answer is None:
            return False
        elif answer:
            self.save_file()
            return not editor.is_modified
        else:
            return True

    def on_close(self):
        for tab in self.notebook.tabs():
            editor = self.notebook.nametowidget(tab).winfo_children()[0]
            if not self.check_save_needed(editor):
                return
        self.root.destroy()

    def jump_to_line(self):
        editor = self.get_current_editor()
        if not editor:
            return
            
        input_text = self.goto_entry.get().strip()
        if input_text:
            editor.jump_to_line(input_text)

    def jump_to_first_line(self):
        editor = self.get_current_editor()
        if editor:
            editor.jump_to_line(1)
            self.goto_entry.delete(0, tk.END)
            self.goto_entry.insert(0, "1")

    def jump_to_last_line(self):
        editor = self.get_current_editor()
        if editor:
            end_index = editor.text_area.index('end-1c')
            last = int(end_index.split('.')[0]) if end_index != '1.0' else 1
            editor.jump_to_line(last)
            self.goto_entry.delete(0, tk.END)
            self.goto_entry.insert(0, str(last))

    def delete_lines(self, direction):
        editor = self.get_current_editor()
        if editor:
            editor.delete_lines(direction)
            editor.text_area.focus_set()

    def add_line(self, direction):
        editor = self.get_current_editor()
        if not editor:
            return
        
        current_pos = editor.text_area.index(tk.INSERT)
        current_line = int(current_pos.split('.')[0])
        
        if direction == -1:
            insert_pos = f"{current_line}.0"
            editor.text_area.insert(insert_pos, "\n")
            new_pos = f"{current_line}.0"
        else:
            insert_pos = f"{current_line + 1}.0"
            editor.text_area.insert(insert_pos, "\n")
            new_pos = f"{current_line + 1}.0"
        
        editor.text_area.mark_set(tk.INSERT, new_pos)
        editor.text_area.see(new_pos)
        editor.is_modified = True
        self.update_title(editor)
        editor.update_line_numbers()
        editor.highlight_current_line()

    def update_status(self, editor):
        current_line, current_col = editor.text_area.index(tk.INSERT).split('.')
        end = editor.text_area.index('end-1c')
        total = int(end.split('.')[0]) if end != '1.0' else 1
        status = f"总行数: {total}"
        if editor.current_file:
            status += f" | 文件: {editor.current_file}"
        self.status_var.set(status)

    def update_title(self, editor):
        tab_id = self.notebook.select()
        base = "未命名" if not editor.current_file else os.path.basename(editor.current_file)
        title = base + (" *" if editor.is_modified else "")
        self.notebook.tab(tab_id, text=title)

    def on_tab_changed(self):
        editor = self.get_current_editor()
        if editor:
            self.update_status(editor)

    def open_search_replace(self):
        SearchReplaceDialog(self.root, self, self.last_search, self.last_replace)


if __name__ == "__main__":
    root = tk.Tk()
    app = MainApplication(root)
    root.geometry("1000x700")
    root.mainloop()










